DynamoDBのput_item()やupdate_item()でConditionalCheckFailedExceptionが発生したとき、キャパシティユニットが消費されるか確認してみた
DynamoDBテーブルでデータの追加や更新をするとき、条件を指定することがよくあります。
- データがすでに存在するなら、更新しない
- 新しいデータの場合だけ、更新したい
- など
この条件を満たさない場合は、ConditionalCheckFailedException
のエラーが発生します。
そこで、ふと気になったのです。条件を満たさないとき(エラー発生したとき)、キャパシティユニットは消費されるのか?と。
というわけで、試してみました。
おすすめの方
- 掲題について知りたい方
- DynamoDBテーブルの条件指定操作の方法を知りたい方
適当なDynamoDBテーブルを用意する
適当なDynamoDBテーブルを用意します。インデックスも存在しません。
実験用のスクリプトを作成し、実行する
それぞれ、作成・更新・削除を「条件指定なし」「条件指定あり(エラー発生する)」で試します。
import boto3 from botocore.exceptions import ClientError dynamodb = boto3.resource("dynamodb") table = dynamodb.Table("boto3-test") def main(): put_item1() put_item2() update_item1() update_item2() delete_item1() delete_item2() def put_item1(): resp = table.put_item( Item={"todoId": "t0001", "name": "りんご飴を買う"}, ReturnConsumedCapacity="TOTAL" ) print(resp["ConsumedCapacity"]) def put_item2(): try: table.put_item( Item={"todoId": "t0001", "name": "わたがしを買う"}, ReturnConsumedCapacity="TOTAL", ConditionExpression="attribute_not_exists(todoId)", ) print(resp["ConsumedCapacity"]) except ClientError as e: if e.response["Error"]["Code"] == "ConditionalCheckFailedException": print(e) # not raise else: raise def update_item1(): resp = table.update_item( Key={"todoId": "t0001"}, UpdateExpression="set #name = :name", ExpressionAttributeNames={ "#name": "name", }, ExpressionAttributeValues={ ":name": "たこやきを買う", }, ReturnConsumedCapacity="TOTAL", ) print(resp["ConsumedCapacity"]) def update_item2(): try: resp = table.update_item( Key={"todoId": "t0001"}, UpdateExpression="set #name = :name", ConditionExpression="attribute_not_exists(todoId)", ExpressionAttributeNames={ "#name": "name", }, ExpressionAttributeValues={ ":name": "かき氷を買う", }, ReturnConsumedCapacity="TOTAL", ) print(resp["ConsumedCapacity"]) except ClientError as e: if e.response["Error"]["Code"] == "ConditionalCheckFailedException": print(e) # not raise else: raise def delete_item1(): resp = table.delete_item( Key={"todoId": "t0001"}, ReturnConsumedCapacity="TOTAL", ) print(resp["ConsumedCapacity"]) def delete_item2(): try: resp = table.delete_item( Key={"todoId": "t0001"}, ConditionExpression="attribute_exists(todoId)", ReturnConsumedCapacity="TOTAL", ) print(resp["ConsumedCapacity"]) except ClientError as e: if e.response["Error"]["Code"] == "ConditionalCheckFailedException": print(e) # not raise else: raise if __name__ == "__main__": main()
スクリプトを実行する
下記でスクリプトを実行します。少なくとも3つのキャパシティユニットが消費されました。
$ python app.py {'TableName': 'boto3-test', 'CapacityUnits': 1.0} An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed {'TableName': 'boto3-test', 'CapacityUnits': 1.0} An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed {'TableName': 'boto3-test', 'CapacityUnits': 1.0} An error occurred (ConditionalCheckFailedException) when calling the DeleteItem operation: The conditional request failed
エラー発生の場合は、Responseを受け取れないので、消費したキャパシティユニットが不明です。 そのため、CloudWatchメトリクスを確認します。
CloudWatchメトリクスで消費キャパシティユニットを確認する
CloudWatchメトリクスで次のメトリクスを確認します。
- ConsumedWriteCapacityUnits
キャパシティユニットの消費合計数は、6でした。 このうち、3つのキャパシティユニットは、条件指定が無い場合の操作です。 つまり、条件指定でエラー発生した場合でも、キャパシティユニットは消費されています。
さいごに
DynamoDBテーブルのアイテムに対する操作で条件を指定し、エラー発生した場合のキャパシティユニット消費数を確認してみました。 「条件が不一致ならキャパシティユニットが表示されない!」と思っていたので、新たな発見でした。 どなたかの参考になれば幸いです。